package cn.changeforyou.web.cloud.blog.exception;

import cn.changeforyou.base.exception.DataExceptionEnum;
import cn.changeforyou.base.exception.JsonException;
import cn.changeforyou.base.exception.SystemExceptionEnum;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;
import org.yaml.snakeyaml.constructor.DuplicateKeyException;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author zhyu
 * @md-model springboot.mvc.exception 全局异常处理
 */
@ControllerAdvice
public class GlobalExceptionHandler {

    Logger log = LoggerFactory.getLogger("exception");

    @ResponseBody
    @ExceptionHandler(value = JsonException.class)
    public ModelAndView handleApiException(JsonException e) {
        if (log.isDebugEnabled()) {
            e.printStackTrace();
        }
        ModelAndView mv = new ModelAndView();
        MappingJackson2JsonView jsonView = new MappingJackson2JsonView();
        mv.setView(jsonView);
        mv.addObject("code", e.getCode());
        mv.addObject("message", e.getMessage());
        return mv;
    }

    final static Pattern PATTERN = Pattern.compile("\\[\"([a-zA-Z0-9]*)\"\\]");

    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public ModelAndView handleException(Exception e, HandlerMethod method) {
        if (log.isDebugEnabled()) {
            e.printStackTrace();
        }
        ModelAndView mv = new ModelAndView();
        boolean restController = AnnotatedElementUtils.hasAnnotation(method.getMethod().getDeclaringClass(), RestController.class);
        ResponseBody responseBody = method.getMethod().getAnnotation(ResponseBody.class);

        if (restController || null != responseBody) {
            //这时需要返回 json
            MappingJackson2JsonView jsonView = new MappingJackson2JsonView();
            mv.setView(jsonView);
            mv.addObject("data", null);
            if (e instanceof BindException) {
                BindingResult bindingResult;
                if (e instanceof MethodArgumentNotValidException) {
                    bindingResult = ((MethodArgumentNotValidException) e).getBindingResult();
                } else {
                    bindingResult = ((BindException) e).getBindingResult();
                }
                FieldError fieldError = bindingResult.getFieldError();
                if (null != fieldError) {
                    String field = fieldError.getField();
                    String defaultMessage = fieldError.getDefaultMessage();
                    String msg = "参数" + field + "异常,  " + defaultMessage;
                    mv.addObject("message", msg);
                    mv.addObject("code", DataExceptionEnum.PARAM_BLANK.getCode());
                } else if (null != bindingResult.getGlobalError()) {
                    ObjectError globalError = bindingResult.getGlobalError();
                    String msg = "参数异常,  " + globalError.getDefaultMessage();
                    mv.addObject("message", msg);
                    mv.addObject("code", DataExceptionEnum.PARAM_BLANK.getCode());
                } else {
                    mv.addObject("message", "参数绑定异常");
                    mv.addObject("code", DataExceptionEnum.PARAM_BLANK.getCode());
                }
            }
        } else if (e instanceof HttpMessageNotReadableException) {
            Throwable cause = e.getCause();
            //注意 InvalidFormatException 是 MismatchedInputException 的子类
            if (cause instanceof InvalidFormatException) {
                InvalidFormatException invalidFormatException = (InvalidFormatException) cause;
                wrapperJsonMappingException(mv, invalidFormatException, "的数据类型错误, 应该是" + invalidFormatException.getTargetType().getSimpleName());
            } else if (cause instanceof MismatchedInputException) {
                MismatchedInputException mismatchedInputException = (MismatchedInputException) cause;
                wrapperJsonMappingException(mv, mismatchedInputException, "应该用嵌套json对象或者数组来传递");
            } else {
                mv.addObject("code", DataExceptionEnum.PARAM_BLANK.getCode());
                mv.addObject("message", "参数类型错误");
            }
        } else if (e instanceof MissingServletRequestParameterException) {
            String parameterName = ((MissingServletRequestParameterException) e).getParameterName();
            String msg = "参数" + parameterName + "没填写, 或者请后端检查是否是json的情况下把@RequestBody写成@RequestParam";
            mv.addObject("message", msg);
            mv.addObject("code", DataExceptionEnum.PARAM_BLANK.getCode());
        } else if (e instanceof DuplicateKeyException) {
            mv.addObject("message", "数据请勿反复提交和修改");
            mv.addObject("code", DataExceptionEnum.PARAM_BLANK.getCode());
        }
        System.err.println("发现没有没有录入的参数异常, 请检查代码");
        log.error("error", e);
        mv.addObject("code", SystemExceptionEnum.SYSTEM_ERROR.getCode());
        mv.addObject("message", SystemExceptionEnum.SYSTEM_ERROR.getMessage());
        return mv;
    }

    private void wrapperJsonMappingException(ModelAndView mv, JsonMappingException jsonMappingException, String message) {
        String pathReference = jsonMappingException.getPathReference();
        Matcher matcher = PATTERN.matcher(pathReference);
        StringBuilder sb = new StringBuilder();
        while (matcher.find()) {
            String group = matcher.group(1);
            sb.append(group).append(".");
        }
        sb.deleteCharAt(sb.length() - 1);
        mv.addObject("code", DataExceptionEnum.PARAM_BLANK.getCode());
        mv.addObject("message", "参数" + sb + message);
    }
}
